﻿using System;
using System.Threading;

namespace MosCraft.Utilities
{
    [Serializable]
    public class FastRandom 
    {
        private long randomSeed;
        private static long seedUnique = 8682522807148012L;
        private static readonly long Multiplier = 0x5DEECE66DL;
        private static readonly long Mask = (1L << 48) - 1;
        private static readonly long Addend = 0xBL;
      
        public FastRandom()
            : this(seedUniquifier() ^ DateTime.Now.ToFileTime())
        {
        }

        public FastRandom(long Seed)
        {
            this.randomSeed = initialScramble(Seed);
        }

        public void setSeed(long Seed)
        {
            this.randomSeed = initialScramble(Seed);
        }

        protected int next(int bits)
        {
            this.randomSeed = (this.randomSeed * Multiplier + Addend) & Mask;

            return (int)(this.randomSeed >> (48 - bits));
        }

        public int nextInt(int n)
        {
            if (n <= 0)
            {
                throw new ArgumentException("n must be positive");
            }

            if ((n & -n) == n)
            {  // i.e., n is a power of 2
                return (int)((n * (long)next(31)) >> 31);
            }

            int bits, val;
            do
            {
                bits = next(31);
                val = bits % n;
            }
            while (bits - val + (n - 1) < 0);
            return val;
        }

        public int nextInt()
        {
            return next(32);
        }

        public void nextBytes(byte[] bytes) {
            for (int i = 0, len = bytes.Length; i < len; )
            {
                for (int rnd = nextInt(),
                         n = Math.Min(len - i, sizeof(Int32) / sizeof(Byte));
                     n-- > 0; rnd >>= sizeof(Byte))
                    bytes[i++] = (byte)rnd;
            }
        }

        public long nextLong()
        {
            return ((long)(next(32)) << 32) + next(32);
        }

        public bool nextBoolean()
        {
            return next(1) != 0;
        }

        public float nextFloat()
        {
            return next(24) / ((float)(1 << 24));
        }

        public double nextDouble()
        {
             return (((long)(next(26)) << 27) + next(27)) / (double)(1L << 53);
        }

        private static long initialScramble(long Seed)
        {
            return (Seed ^ Multiplier) & Mask;
        }

        private static long seedUniquifier() {
        // L'Ecuyer, "Tables of Linear Congruential Generators of
        // Different Sizes and Good Lattice Structure", 1999
            for (;;) {
                long current = seedUnique;
                long next = current * 181783497276652981L;

                if (next != Interlocked.CompareExchange(ref seedUnique, next, current))
                {
                    return next;
                }
            }
        }
    }
}
